package com.zhu.rtda.heap;

import com.zhu.classfile.MemberInfo;
import com.zhu.classfile.attribute.CodeAttribute;

/**
 * Method 表示 Java 类中的一个方法。
 * 它包含方法的元数据信息，如名称、描述符、访问标志、字节码、操作数栈大小、局部变量表大小等。
 * 该类的设计直接对应 class 文件中的方法表（methods[]）条目。
 */
public class Method extends ClassMember {

    /**
     * 当前方法的操作数栈（Operand Stack）在运行时所需的最大深度。
     * 由编译器计算得出，JVM 在执行方法前会据此分配栈帧中的操作数栈空间。
     */
    public int maxStack;

    /**
     * 当前方法的局部变量表（Local Variables）所需的槽数（slot count）。
     * 包括方法参数（实例方法包含 'this'）和方法体内的局部变量。
     * 每个 slot 占 32 位，long 和 double 类型占用两个 slot。
     */
    public int maxLocals;

    /**
     * 方法的字节码指令序列（Java bytecode instructions）。
     * 这是方法的“可执行代码”，JVM 的解释器将逐条执行这些指令。
     * 如果是 native 方法，则该数组为空或为 null。
     */
    public byte[] code;

    /**
     * 从 ClassBinary 中的方法信息数组创建方法数组。
     * 该方法用于类加载过程中，将字节码中的方法信息转换为运行时可用的 Method 对象数组。
     *
     * @param clazz      方法所属的类
     * @param cfMethods  ClassBinary 中的方法信息数组（来自方法表）
     * @return           创建的 Method 对象数组
     */

    public int argSlotCount;

    public static Method[] newMethods(InstanceKlass clazz, MemberInfo[] cfMethods) {
        Method[] methods = new Method[cfMethods.length];
        for (int i = 0; i < cfMethods.length; i++) {
            methods[i] = new Method();
            methods[i].clazz = clazz;
            methods[i].copyMemberInfo(cfMethods[i]);
            methods[i].copyAttributes(cfMethods[i]);
            methods[i].argSlotCount=methods[i].calcArgSlotCount();
        }
        return methods;
    }

    /**
     * 从方法的 class 文件信息中复制属性相关数据。
     * 目前只处理 Code 属性，提取 maxStack、maxLocals 和字节码。
     *
     * @param cfMethod 字节码中的方法信息
     */
    private void copyAttributes(MemberInfo cfMethod) {
        CodeAttribute codeAttr = cfMethod.getCodeAttribute();
        if (codeAttr != null) {
            this.maxStack = codeAttr.getMaxStack();
            this.maxLocals = codeAttr.getMaxLocals();
            this.code = codeAttr.getCode();
        }
    }

    // ========== 访问标志查询 ==========

    /**
     * 判断该方法是否为 synchronized 方法。
     * synchronized 方法在调用前会自动获取对象锁（实例方法）或类锁（静态方法）。
     *
     * @return 如果是 synchronized 方法则返回 true
     */
    public boolean isSynchronized() {
        return (accessFlags & AccessFlags.ACC_SYNCHRONIZED.getValue()) != 0;
    }

    /**
     * 判断该方法是否为桥接方法（Bridge Method）。
     * 桥接方法由编译器生成，用于解决泛型类型擦除带来的多态问题。
     *
     * @return 如果是桥接方法则返回 true
     */
    public boolean isBridge() {
        return (accessFlags & AccessFlags.ACC_BRIDGE.getValue()) != 0;
    }

    /**
     * 判断该方法是否接受可变数量的参数（varargs）。
     * 如：void print(String... args)
     *
     * @return 如果是 varargs 方法则返回 true
     */
    public boolean isVarargs() {
        return (accessFlags & AccessFlags.ACC_VARARGS.getValue()) != 0;
    }

    /**
     * 判断该方法是否为 native 方法。
     * native 方法由 JVM 本地代码实现（如 C/C++），无 Java 字节码。
     *
     * @return 如果是 native 方法则返回 true
     */
    public boolean isNative() {
        return (accessFlags & AccessFlags.ACC_NATIVE.getValue()) != 0;
    }

    /**
     * 判断该方法是否为 abstract 方法。
     * abstract 方法没有方法体，必须在子类中被实现。
     * 只能在 abstract 类或接口中声明。
     *
     * @return 如果是 abstract 方法则返回 true
     */
    public boolean isAbstract() {
        return (accessFlags & AccessFlags.ACC_ABSTRACT.getValue()) != 0;
    }

    /**
     * 判断该方法是否为 strict 模式（strictfp）。
     * strictfp 方法要求浮点运算必须严格遵循 IEEE 754 规范，禁止使用扩展精度。
     *
     * @return 如果是 strict 模式则返回 true
     */
    public boolean isStrict() {
        return (accessFlags & AccessFlags.ACC_STRICT.getValue()) != 0;
    }

    // ========== Getter 方法 ==========

    /**
     * 获取该方法操作数栈的最大深度。
     *
     * @return max_stack 值
     */
    public int getMaxStack() {
        return maxStack;
    }

    /**
     * 获取该方法局部变量表所需的槽数。
     *
     * @return max_locals 值
     */
    public int getMaxLocals() {
        return maxLocals;
    }

    /**
     * 获取该方法的字节码指令数组。
     *
     * @return 字节码数组，若为 native 方法则可能为空
     */
    public byte[] getCode() {
        return code;
    }

    // ========== 辅助方法 ==========

    /**
     * 判断该方法是否为静态方法（static method）。
     * 静态方法属于类，调用时不需要对象实例。
     *
     * @return 如果是 static 方法则返回 true
     */
    public boolean isStatic() {
        return (accessFlags & AccessFlags.ACC_STATIC.getValue()) != 0;
    }

    /**
     * 判断该方法是否为公共方法（public method）。
     *
     * @return 如果是 public 方法则返回 true
     */
    public boolean isPublic() {
        return (accessFlags & AccessFlags.ACC_PUBLIC.getValue()) != 0;
    }

    /**
     * 判断该方法是否为私有方法（private method）。
     *
     * @return 如果是 private 方法则返回 true
     */
    public boolean isPrivate() {
        return (accessFlags & AccessFlags.ACC_PRIVATE.getValue()) != 0;
    }

    /**
     * 判断该方法是否为构造函数（<init>）或类初始化方法（<clinit>）。
     *
     * @return 如果是初始化方法则返回 true
     */
    public boolean isInit() {
        return "<init>".equals(name) || "<clinit>".equals(name);
    }

    /**
     * 获取方法的参数数量（按槽位计算）。
     * long 和 double 类型参数占两个槽位，其余占一个。
     * 实例方法隐含一个 'this' 参数。
     *
     * @return 参数所需的槽位总数
     */

    public String getName(){
        return name;
    }

    public String getDescriptor(){
        return descriptor;
    }

    /**
     * 根据方法描述符（descriptor）计算该方法参数所需的局部变量表槽位总数。
     *
     * <p>规则如下：
     * <ul>
     *   <li>基本类型（除 long/double）占 1 个槽位；
     *   <li>{@code long}（'J'）和 {@code double}（'D'）占 2 个槽位；
     *   <li>引用类型（对象、数组）占 1 个槽位；
     *   <li>实例方法隐含一个 {@code this} 参数（类型为当前类），占 1 个槽位；
     *   <li>静态方法无 {@code this} 参数。
     * </ul>
     *
     * <p>方法描述符格式示例：
     * <ul>
     *   <li>{@code ()V} → 无参 void 方法
     *   <li>{@code (I)V} → 接收一个 int
     *   <li>{@code (J)D} → 接收一个 long，返回 double
     *   <li>{@code ([Ljava/lang/String;)V} → 接收 String[] 数组
     *   <li>{@code (Ljava/lang/Object;I)V} → 接收 Object 和 int
     * </ul>
     *
     * @return 参数所需的槽位总数（包括隐含的 'this'）
     */
    public int calcArgSlotCount() {
        // 初始化槽位计数器
        int argSlotCount = 0;
        // 获取方法描述符，例如 "(ILjava/lang/Object;)V"
        String descriptor = this.descriptor;
        // 从索引 1 开始（跳过开头的 '('）
        int i = 1;

        // 遍历描述符，直到遇到 ')' 表示参数列表结束
        while (i < descriptor.length()) {
            char c = descriptor.charAt(i);

            // 遇到 ')' 表示参数列表结束，跳出循环
            if (c == ')') {
                break;
            }

            // 处理 long ('J') 和 double ('D')：占两个槽位
            if (c == 'J' || c == 'D') {
                argSlotCount += 2;
                i++; // 移动到下一个字符
            }
            // 处理对象引用类型：以 'L' 开头，以 ';' 结尾，如 "Ljava/lang/Object;"
            else if (c == 'L') {
                argSlotCount += 1; // 引用类型占 1 槽
                // 查找对应的分号 ';'
                int semicolonIndex = descriptor.indexOf(';', i);
                if (semicolonIndex == -1) {
                    throw new IllegalArgumentException("Invalid method descriptor: missing ';' in object type");
                }
                i = semicolonIndex + 1; // 跳到 ';' 之后
            }
            // 处理数组类型：以 '[' 开头，后面跟基本类型或对象类型
            else if (c == '[') {
                argSlotCount += 1; // 任何数组都是引用，占 1 槽
                i++; // 跳过当前 '['
                // 跳过多维数组的连续 '['，例如 "[[I" 中有两个 '['
                while (i < descriptor.length() && descriptor.charAt(i) == '[') {
                    i++;
                }
                // 如果数组元素是对象（如 "[Ljava/lang/String;"），需跳过 "L...;"
                if (i < descriptor.length() && descriptor.charAt(i) == 'L') {
                    int semicolonIndex = descriptor.indexOf(';', i);
                    if (semicolonIndex == -1) {
                        throw new IllegalArgumentException("Invalid method descriptor: missing ';' in array element type");
                    }
                    i = semicolonIndex + 1;
                } else {
                    // 数组元素是基本类型（如 "[I", "[[F"），只需再前进一位
                    i++;
                }
            }
            // 处理其他基本类型：I, F, C, B, S, Z 等，均占 1 槽
            else {
                argSlotCount += 1;
                i++;
            }
        }

        // 如果是实例方法（非 static），需要加上隐含的 'this' 参数（占 1 槽）
        if (!isStatic()) {
            argSlotCount += 1;
        }

        return argSlotCount;
    }
}
